home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 October: Mac OS SDK / Dev.CD Oct 96 SDK / Dev.CD Oct 96 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc Source Code / DocShell / ShellMem.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-22  |  9.4 KB  |  343 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        ShellMem.cpp
  3.  
  4.     Contains:    Memory management for the Shell
  5.  
  6.     Owned by:    Nick Pilch
  7.  
  8.     Copyright:    © 1995 - 1996 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.          <4>    .04.1996    NP        1339832: Send exit event before closing
  13.                                     documents/windows from LowMemoryAlert.
  14.          <3>      4/4/96    NP        1338241: Unstaticize function.
  15.          <2>     3/29/96    DM        1334273: show low mem quit alert and then
  16.                                     exit to shell when unable to show another
  17.                                     low mem alert due to out-of-mem err
  18.          <1>    10/24/95    jpa        first checked in
  19. */
  20.  
  21.  
  22. #ifndef _RLSHELL_
  23. #include "RlShell.h"
  24. #endif
  25.  
  26. #ifndef _SHELLDEF_
  27. #include "ShellDef.h"
  28. #endif
  29.  
  30. #ifndef _DISPTCH_
  31. #include <Disptch.xh>
  32. #endif
  33.  
  34. #ifndef _MEMMGR_
  35. #include <MemMgr.h>
  36. #endif
  37.  
  38. #ifndef _DLOGUTIL_
  39. #include <DlogUtil.h>
  40. #endif
  41.  
  42. #ifndef _DOCUTILS_
  43. #include <DocUtils.h>
  44. #endif
  45.  
  46. #ifndef _USERSRCM_
  47. #include <UseRsrcM.h>
  48. #endif
  49.  
  50. #ifndef _TEMPITER_
  51. #include <TempIter.h>
  52. #endif
  53.  
  54. #ifndef _SHELLMEM_
  55. #include <ShellMem.h>
  56. #endif
  57.  
  58. //==============================================================================
  59. // CONSTANTS
  60. //==============================================================================
  61.  
  62. const size_t kSlushFundSize            = 10 * 1024;    // Size of memory slush-fund
  63. const size_t kSlushFundAllocLimit    =  2 * 1024;    // Max request that will free slush fund
  64.  
  65. const ODSize kGoodAppFreeSpace =      64 * 1024;    // Sizes below which we notify the user
  66. const ODSize kGoodAppContigSpace =       8 * 1024;
  67. const ODSize kGoodTempFreeSpace =     100 * 1024;
  68. const ODSize kGoodTempContigSpace =   32 * 1024;
  69.  
  70. const ODSize kBailAppFreeSpace =       2 * 1024;    // App-heap mem requirement of low-mem alert
  71.  
  72.  
  73. //==============================================================================
  74. // METHODS
  75. //==============================================================================
  76.  
  77.  
  78. //-------------------------------------------------------------------------------------
  79. // InitMemory
  80. //-------------------------------------------------------------------------------------
  81.  
  82. void
  83. RealShell::InitMemory( )
  84. {
  85.     // Preload low-mem alerts so we'll be able to use them when space is low:
  86.     CUsingLibraryResources r;
  87.     ::Get1Resource('ALRT',kSHLphSpaceIsLow);
  88.     ::Get1Resource('DITL',kSHLphSpaceIsLow);
  89.     ::Get1Resource('ALRT',kSHLphAppSpaceIsLow);
  90.     ::Get1Resource('DITL',kSHLphAppSpaceIsLow);
  91.     
  92.     if( ! MMAllocateSlushFund(kDefaultHeapID, kSlushFundSize,kSlushFundAllocLimit) )
  93.         WARN("Could not allocate slush fund");
  94. }
  95.  
  96.  
  97. //-------------------------------------------------------------------------------------
  98. // IsFreeMemoryLow
  99. //-------------------------------------------------------------------------------------
  100.  
  101. inline ODSLong Max( ODSLong a, ODSLong b )
  102. {return a>b ?a :b;}
  103.     
  104. ODSize RealShell::IsFreeMemoryLow( ODBoolean &appIsLow, ODBoolean &tempIsLow )
  105. {
  106.     // First check/replenish the memory slush fund:
  107.     ODBoolean slushy;
  108.     if( MMSlushFundSize(kDefaultHeapID) > 0 )
  109.         slushy = kODTrue;
  110.     else
  111.         slushy = MMAllocateSlushFund(kDefaultHeapID, kSlushFundSize,kSlushFundAllocLimit);
  112.     
  113.     size_t free,contig;
  114.     ODSLong freeDelta, contigDelta;
  115.     ODSLong purgeApp = 0, purgeTemp = 0;
  116.     
  117.     MMSystemFreeSpace(kMMAppMemory, &free,&contig);
  118.     freeDelta = kGoodAppFreeSpace-free;
  119.     contigDelta = kGoodAppContigSpace-contig;
  120.     purgeApp = Max(freeDelta,contigDelta);
  121.     appIsLow = (purgeApp>0);
  122.     
  123.     MMSystemFreeSpace(kMMTempMemory, &free,&contig);
  124.     freeDelta = kGoodTempFreeSpace-free;
  125.     contigDelta = kGoodTempContigSpace-contig;
  126.     purgeTemp = Max(freeDelta,contigDelta);
  127.     tempIsLow = (purgeTemp>0);
  128.     
  129.     if( !slushy )
  130.         tempIsLow = kODTrue;
  131.     
  132.     purgeApp = Max(purgeApp,purgeTemp);
  133.     return (purgeApp>0 ?purgeApp :0);
  134. }
  135.  
  136.  
  137. //-------------------------------------------------------------------------------------
  138. // CheckFreeMemory
  139. //-------------------------------------------------------------------------------------
  140.  
  141. ODBoolean RealShell::CheckFreeMemory( )
  142. {
  143.     // If space is low, purge. If space is still low, alert the user, provided this
  144.     // process is active. After the user is alerted, set a flag so we don't put up
  145.     // an endless stream of alerts. Note that we don't use the normal dialog filter,
  146.     // since it calls the Dispatcher, which may cause trouble when space is low.
  147.     
  148.     ODBoolean appIsLow, tempIsLow;
  149.     ODSize purge = this->IsFreeMemoryLow(appIsLow,tempIsLow);
  150.     
  151.     if( purge ) {
  152.         // Low on free space, so purge:
  153.         this->Purge( 2*purge );
  154.         
  155.         this->IsFreeMemoryLow(appIsLow,tempIsLow);
  156.         
  157.         if( (appIsLow || tempIsLow) ) {
  158.             // Yow, still low on memory after purging.
  159.             size_t freeApp,contig;
  160.             MMSystemFreeSpace(kMMAppMemory, &freeApp,&contig);
  161.             if( !fLowMemNotified && fProcessIsActive && freeApp > kBailAppFreeSpace ) {
  162.                 this->LowMemoryAlert( tempIsLow );        // Notify user 1st time if we can
  163.                 fLowMemNotified = kODTrue;
  164.             }
  165.             return kODFalse;
  166.         }
  167.     }
  168.     fLowMemNotified = kODFalse;        // We're okay now, clear notification state
  169.     return kODTrue;
  170. }
  171.  
  172.  
  173. static ODBoolean
  174. DocNeedsSaving( Environment *ev, ODSession *session, ODDocument *document )
  175. {
  176.     // Throws no exceptions.
  177.     TRY{
  178.         return ODDocumentHasChanges(ev,session,document);
  179.     }CATCH_ALL{
  180.         WARN("ODDocumentHasChanges failed, err %d",ErrorCode());
  181.     }ENDTRY
  182.     return kODFalse;
  183. }
  184.  
  185. #if ODDebug
  186. void BREAK( const char[] );
  187. #endif
  188.  
  189. void ODShellLowMemoryGoodbye()
  190. {
  191. #if ODDebug
  192.     BREAK("about to show fatal low memory alert...");
  193. #endif
  194.     {
  195.         TRY{
  196.             CUsingLibraryResources r;
  197.             InitCursor();
  198.             ::Alert(kODAlertShellLowMemoryError, kODNULL);
  199.         }CATCH_ALL{
  200.             WARN("cannot show fatal low memory alert - quitting...");
  201.         }ENDTRY
  202.     }
  203.     // make sure CUsingLibraryResources goes out of scope first...
  204.     ::ExitToShell(); // bye bye process
  205. }
  206.  
  207. //------------------------------------------------------------------------------
  208. // DispatchExitEvent
  209. //
  210. //    Duplicated in ODSessnB.cpp
  211. //------------------------------------------------------------------------------
  212.  
  213. static void    DispatchExitEvent(Environment* ev, ODDispatcher* dispatcher)
  214. {
  215. // Copied from RealShell::FakePrintMenuEvent
  216.     ODEventData event;
  217.     event.message = 0L;
  218.     event.what = kODEvtExit;
  219.     // zero the rest of the fields
  220.     WASSERT( sizeof(Point) == sizeof(long) );
  221.     *(long*)&event.where = 0L;
  222.     event.when = 0L;
  223.     event.modifiers = 0;
  224.     dispatcher->Dispatch(ev, &event);
  225. }
  226.  
  227. //------------------------------------------------------------------------------
  228. // RealShell::LowMemoryAlert
  229. //------------------------------------------------------------------------------
  230.  
  231. void RealShell::LowMemoryAlert( ODBoolean tempMem )
  232. {
  233.     /*    Do not use the regular ODDialogFilter for this alert, as it calls back to the
  234.         Dispatcher, which calls other OpenDoc routines. This could be dangerous/fatal
  235.         in precisely this kind of low memory situation. */
  236.         
  237.     // First, do any open documents need to be saved?
  238.     ODBoolean needSave = kODFalse;
  239.     TRY{
  240.         for( TempODWindowIterator wi(fEV,fSession->GetWindowState(fEV)); wi; ++wi )
  241.             if( DocNeedsSaving(fEV,fSession, ODGetDraftOfWindow(fEV,wi)->GetDocument(fEV)) ) {
  242.                 needSave = kODTrue;
  243.                 break;
  244.             }
  245.     }CATCH_ALL{
  246.         if ( ErrorCode() == kODErrOutOfMemory )
  247.         {
  248.             ODShellLowMemoryGoodbye(); // goodbye cruel world
  249.         }
  250.         else
  251.         {
  252.             WARN("Error %d checking docs",ErrorCode());
  253.             // don't reraise
  254.         }
  255.     }ENDTRY
  256.  
  257.     ODSShort result; ODVolatile(result);
  258.     TRY{
  259.         // Shazam! Show the alert:
  260.         ODSShort id = needSave ?kSHLphSpaceIsLow :kSHLphSpaceIsLowNoSave;
  261.         if( tempMem ) id++;
  262.         {
  263.             CUsingLibraryResources r;
  264.             result = ShowAlert(fEV, id, kODNULL, fSession);
  265.         }
  266.     }CATCH_ALL{
  267.         if ( ErrorCode() == kODErrOutOfMemory )
  268.         {
  269.             ODShellLowMemoryGoodbye(); // goodbye cruel world
  270.         }
  271.         else
  272.         {
  273.             WARN("Error %d showing low mem alert",ErrorCode());
  274.             // don't reraise
  275.         }
  276.     }ENDTRY
  277.     
  278.     if( result == kStdCancelItemIndex )
  279.         return;
  280.     
  281.     needSave = needSave && (result==kStdOkItemIndex);    // Don't save if user said not to
  282.  
  283.     // CYBERDOG NEEDS THIS EXIT EVENT FIRST IF WE'RE GOING TO SHUT DOWN
  284.     //    EVERYTHING.
  285.     TRY
  286.         DispatchExitEvent(fEV, fSession->GetDispatcher(fEV));
  287.     CATCH_ALL
  288.         WARN("Caught error trying to dispatch exit event in "
  289.                 "RealShell::LowMemoryAlert");
  290.         if ( ErrorCode() == kODErrOutOfMemory )
  291.             ODShellLowMemoryGoodbye(); // quit immediately
  292.     ENDTRY
  293.     
  294.     // First close documents. (If we're told to save, close only the unmodified docs.)
  295.     // Note that this may free up memory before the save step below.
  296.     for( TempODWindowIterator wi(fEV,fSession->GetWindowState(fEV)); wi; ++wi ) {
  297.         TRY{
  298.             ODDocument *document = ODGetDraftOfWindow(fEV,wi)->GetDocument(fEV);
  299.             if( !needSave || !DocNeedsSaving(fEV,fSession, document) )
  300.                 ODCloseDocument(fEV, fSession, document);
  301.         }CATCH_ALL{
  302.             WARN("Error %d closing doc",ErrorCode());
  303.             // don't reraise
  304.         }ENDTRY
  305.     }
  306.     
  307.     if( needSave ) {
  308.         // Now save&close the rest of the documents.
  309.         for( TempODWindowIterator wi(fEV,fSession->GetWindowState(fEV)); wi; ++wi ) {
  310.             TRY{
  311.                 ODDocument *document = ODGetDraftOfWindow(fEV,wi)->GetDocument(fEV);
  312.                 if( DocNeedsSaving(fEV,fSession, document) ) {
  313.                     /*    Do not call RealShell::Save, as this will put up a standard-file dialog
  314.                         for an unsaved new document. Not the safest thing to do right now! */
  315.                     ODSaveDocument(fEV,fSession, document);
  316.                 }
  317.             }CATCH_ALL{
  318.                 WARN("Error %d saving/closing doc",ErrorCode());
  319.                 // don't reraise
  320.             }ENDTRY
  321.         }
  322.     }
  323.  
  324.     fSession->GetDispatcher(fEV)->Exit(fEV);        // Quit at next event loop
  325. }
  326.  
  327.  
  328. //-------------------------------------------------------------------------------------
  329. // Purge
  330. //-------------------------------------------------------------------------------------
  331.  
  332. ODSize    RealShell::Purge(ODSize size)
  333. {
  334.     ODSize result;
  335.     TRY{
  336.         result= fSession->Purge(fEV,size);
  337.     }CATCH_ALL{
  338.         result = 0;
  339.         // do not reraise, ignore exception
  340.     }ENDTRY
  341.     return result;
  342. }
  343.